(function($) {

    $.fn.vpSpWebPartPickerEx = function(options) {

        var suggestedWebPartsCache;
        var allWebPartsCache;
        var listWebPartsCache;

        var defOption = {

            suggestedWebPartsTableId: '#ctl00_PlaceHolderDialogBodySection_SuggestedWebPartsTable__WebPartTable',
            allWebPartTableId: '#ctl00_PlaceHolderDialogBodySection_AllWebPartTable__WebPartTable',
            listWebPartTableId: '#ctl00_PlaceHolderDialogBodySection_ListWebPartsTable__WebPartTable',

            suggestedWebPartsTableHeaderId: '#ctl00_PlaceHolderDialogBodySection_SuggestedWebPartsTable__HeaderTable',
            allWebPartTableHeaderId: '#ctl00_PlaceHolderDialogBodySection_AllWebPartTable__HeaderTable',
            listWebPartTableHeaderId: '#ctl00_PlaceHolderDialogBodySection_ListWebPartsTable__HeaderTable',

            suggestedWebPartsTableExcImageId: "#ctl00_PlaceHolderDialogBodySection_SuggestedWebPartsTable__HeaderTable_ExpandCollapseImage",
            allWebPartTableHeaderExcImageId: "#ctl00_PlaceHolderDialogBodySection_AllWebPartTable__HeaderTable_ExpandCollapseImage",
            listWebPartTableHeaderExcImageId: "#ctl00_PlaceHolderDialogBodySection_ListWebPartsTable__HeaderTable_ExpandCollapseImage",

            webpartPickerTableId: '#WebpartPickerTable',
            quickSearchUIId: '#quickSearch',

            spImagePlus: '/_layouts/images/plus.gif',
            spImageMinus: '/_layouts/images/minus.gif',

            quickSearchUIHtml: "<table class='ms-webpartpickerpagebackground' id='quickSearchTable' cellspacing=0 cellpadding=0 width='100%'>" +
                                  " <tr style='height:15px' colspan='3'><td>&nbsp;</td></tr>" +
                                  "     <tr>" +
                                            "<td width='15px'>&nbsp;</td>" +
                                        "<td>" +
                                            "<input type='text' id='quickSearch' name='quickSearch' style='width:100%'>" +
                                        "</td>" +
                                        "<td width='15px'>&nbsp;</td>" +
                                    "</tr>" +
                                "</table>",
            selectors: {
                isPartLine: 'input',
                isPartSeparator: 'td.ms-WPTitle',
                getText: 'td.ms-webpartpickerpagetext'
            }
        };

        var opts = $.extend(defOption, options);

        InitQuickSearch();

        function InitQuickSearch() {

            var pickerContainer = $(opts.webpartPickerTableId);

            $(opts.quickSearchUIHtml).insertBefore(pickerContainer);

            initCache();

            $(opts.quickSearchUIId).keyup(function() {

                var value = $(this).val();

                value = value.toString().toLowerCase();

                var hasAnyAllWebPartVisibleElements = false;
                var hasListWebPartVisibleElements = false;
                var hasSuggestedWebPartVisibleElements = false;

                /// search all web part section
                $(allWebPartsCache.elements).each(function(n, allVal) {

                    var hasAllWebPartVisibleElements = false;

                    $(allVal.elements).each(function(n, rVal) {

                        var text = getText(rVal);

                        if (text) {

                            text = text.toString().toLowerCase();

                            if (text.indexOf(value) != -1) {

                                hasAllWebPartVisibleElements = true;
                                hasAnyAllWebPartVisibleElements = true;

                                showWebPartLine(rVal);
                            }
                            else {
                                hideWebPartLine(rVal);
                            }
                        }
                    });

                    hasAllWebPartVisibleElements ? $(allVal.sep).show() : $(allVal.sep).hide();
                });

                setCacheElementVisible(allWebPartsCache, hasAnyAllWebPartVisibleElements);

                /// search suggested web part section
                $(suggestedWebPartsCache.elements).each(function(n, sVal) {

                    var text = getText(sVal);

                    if (text) {

                        text = text.toString().toLowerCase();

                        if (text.indexOf(value) != -1) {
                            showWebPartLine(sVal);
                            hasSuggestedWebPartVisibleElements = sVal;
                        }
                        else {
                            hideWebPartLine(sVal);
                        }
                    }
                });

                setCacheElementVisible(suggestedWebPartsCache, hasSuggestedWebPartVisibleElements);

                /// search list web part section
                $(listWebPartsCache.elements).each(function(n, sVal) {

                    var text = getText(sVal);

                    if (text) {

                        text = text.toString().toLowerCase();

                        if (text.indexOf(value) != -1) {
                            showWebPartLine(sVal);
                            hasListWebPartVisibleElements = true;
                        }
                        else {
                            hideWebPartLine(sVal);
                        }
                    }
                });

                setCacheElementVisible(listWebPartsCache, hasListWebPartVisibleElements);
            });
        }

        function initCache() {

            suggestedWebPartsCache = {
                table: null,
                elements: new Array(),
                header: $(opts.suggestedWebPartsTableHeaderId),
                body: $(opts.suggestedWebPartsTableId),
                collapseImg: $(opts.suggestedWebPartsTableExcImageId)
            };

            allWebPartsCache = {
                table: null,
                elements: new Array(),
                header: $(opts.allWebPartTableHeaderId),
                body: $(opts.allWebPartTableId),
                collapseImg: $(opts.allWebPartTableHeaderExcImageId)
            };

            listWebPartsCache =
            {
                table: null,
                elements: new Array(),
                header: $(opts.listWebPartTableHeaderId),
                body: $(opts.listWebPartTableId),
                collapseImg: $(opts.listWebPartTableHeaderExcImageId)
            };

            $(opts.listWebPartTableId).each(function() {
                $('tr', this).each(function() {
                    if (isPartLine(this)) {
                        listWebPartsCache.elements.push(this);
                    }
                });
            });

            $(opts.suggestedWebPartsTableId).each(function() {
                $('tr', this).each(function() {
                    if (isPartLine(this)) {
                        suggestedWebPartsCache.elements.push(this);
                    }
                });
            });

            $(opts.allWebPartTableId).each(function() {

                var lastSeparator = null;

                $('tr', this).each(function(n, allVal) {

                    if (isPartSeparator(allVal)) {

                        if (lastSeparator == null) {
                            lastSeparator = { sep: allVal, elements: new Array() };
                        }

                        if (lastSeparator.sep != this) {
                            lastSeparator = { sep: allVal, elements: new Array() };
                        }

                        allWebPartsCache.elements.push(lastSeparator);
                    }
                    else {

                        if (isPartLine(allVal) && (lastSeparator != null)) {
                            lastSeparator.elements.push(allVal);
                        }
                    }
                });
            });
        }

        function setCacheElementVisible(e, visible) {
            if (visible) {
                $(e.header).show();
                $(e.body).show();
                $(e.collapseImg).attr('src', opts.spImageMinus);
            } else {
                $(e.header).hide();
                $(e.body).hide();
                $(e.collapseImg).attr('src', opts.spImagePlus);
            }
        }

        function isPartLine(e) {
            return $(opts.selectors.isPartLine, e).length > 0;
        }

        function isPartSeparator(e) {
            return $(opts.selectors.isPartSeparator, e).length > 0;
        }

        function getText(e) {
            return $(opts.selectors.getText, e).html();
        }

        function hideWebPartLine(e) {
            $(e).hide()
            .next('tr').hide();
        }

        function showWebPartLine(e) {
            $(e).show()
            .next('tr').show();
        }
    };
})(jQuery);